//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <bsp.h>
#include <uart.h>

EXTERN_C bool_t CDECL BspSetupComPort(
                ComPort comPort, const ComPortSettings *pSettings)
{
    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    ComPortSettings defaultSettings = {
        115200,
        ComPortByteSize_Default,
        ComPortParity_Default,
        ComPortStopBits_Default
    };

    if (NULL == pSettings) pSettings = &defaultSettings;

    // Make the line parameter
    uint32_t uUBRLCR = _UFIFOEN;

    switch (pSettings->byteSize) {
        case ComPortByteSize_5:     uUBRLCR |= _WRDLEN_5;   break;
        case ComPortByteSize_6:     uUBRLCR |= _WRDLEN_6;   break;
        case ComPortByteSize_7:     uUBRLCR |= _WRDLEN_7;   break;
        case ComPortByteSize_8:     uUBRLCR |= _WRDLEN_8;   break;
        default: return FALSE;
    }

    switch (pSettings->stopBits) {
        case ComPortStopBits_One:                           break;
        case ComPortStopBits_One5:  uUBRLCR |= _XSTOP;      break;
        case ComPortStopBits_Two:   uUBRLCR |= _XSTOP;      break;
        default: return FALSE;
    }

    switch (pSettings->parity) {
        case ComPortParity_No:                                      break;
        case ComPortParity_Odd:     uUBRLCR |= _PRTEN;              break;
        case ComPortParity_Even:    uUBRLCR |= _PRTEN | _EVENPRT;   break;
        case ComPortParity_Mark:    return FALSE;
        case ComPortParity_Space:   return FALSE;
        default: return FALSE;
    }

    // Make the baudrate divisor
    uint16_t u16Divisor = 0;
    switch (pSettings->uBaudRate) {
        case 110:       u16Divisor = _BRD_110_16;       break;
        case 1200:      u16Divisor = _BRD_1200_16;      break;
        case 2400:      u16Divisor = _BRD_2400_16;      break;
        case 9600:      u16Divisor = _BRD_9600_16;      break;
        case 14400:     u16Divisor = _BRD_14400_16;     break;
        case 19200:     u16Divisor = _BRD_19200_16;     break;
        case 38400:     u16Divisor = _BRD_38400_16;     break;
        case 56000:     u16Divisor = _BRD_57600_16;     break;
        case 57600:     u16Divisor = _BRD_76800_16;     break;
        case 115200:    u16Divisor = _BRD_115200_16;    break;
        default: return FALSE;
    }

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    // Enable uart
    Outl(_SYSCON(iobase), Inl(_SYSCON(iobase)) | _UARTEN);

    // Disable uart interrupt
    Outw(_INTMR(iobase), Inw(_INTMR(iobase)) & 0x8fff);

    // Set baut rate and line parameters
    Outl(_UBRLCR(iobase), uUBRLCR | u16Divisor);

    // Clear up uart fifo
    while (!(Inl(_SYSFLG(iobase)) & _URXFE)) {
        Inw(_UARTDR(iobase));
    }

    return TRUE;
}

EXTERN_C bool_t CDECL BspWriteComPort(ComPort comPort, uint8_t u8Data)
{
    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    while (Inl(_SYSFLG(iobase)) & _UTXFF);

    Outb(_UARTDR(iobase), u8Data);

    return TRUE;
}

EXTERN_C bool_t CDECL BspReadComPort(ComPort comPort, uint8_t *pu8Data)
{
    if (NULL == pu8Data) return FALSE;

    *pu8Data = 0;

    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    while (Inl(_SYSFLG(iobase)) & _URXFE);

    uint16_t u16Data = Inw(_UARTDR(iobase));

    if (u16Data & (_FRAME | _PARITY | _OVERRUN)) return FALSE;

    *pu8Data = u16Data & 0xff;
    return TRUE;
}

EXTERN_C bool_t CDECL TryReadComPort(
                ComPort comPort, uint8_t *pu8Data, uint_t uTimes)
{
    if (NULL == pu8Data) return FALSE;

    *pu8Data = 0;

    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    while (Inl(_SYSFLG(iobase)) & _URXFE) {
        if (0 == uTimes--) return FALSE;
    }

    uint16_t u16Data = Inw(_UARTDR(iobase));

    if (u16Data & (_FRAME | _PARITY | _OVERRUN)) return FALSE;

    *pu8Data = u16Data & 0xff;
    return TRUE;
}
